package multiIntro;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import abc.aspectj.ast.AJNodeFactory;
import abc.aspectj.ast.IntertypeDecl;
import abc.aspectj.ast.IntertypeMethodDecl;
import abc.aspectj.types.AJTypeSystem;
import abc.aspectj.visit.AspectMethods;

import polyglot.ast.Block;
import polyglot.ast.ClassMember;
import polyglot.ast.Expr;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Term;
import polyglot.ast.TypeNode;
import polyglot.ext.jl.ast.Term_c;
import polyglot.lex.Identifier;
import polyglot.types.Flags;
import polyglot.types.TypeSystem;
import polyglot.util.Position;
import polyglot.util.TypedList;
import polyglot.visit.CFGBuilder;
import polyglot.visit.NodeVisitor;
import xaj.xc.ast.ListNode;
import xaj.xc.ast.NodeXAJ;
import xaj.xc.ast.XAJNodeFactory;

//importsymbol multiIntro.BlockEval;

public class MultiIntro extends Term_c implements IntertypeDecl, NodeXAJ, xaj.lang.Map {
	/*@grammar extends intertype_member_declaration using BlockEval {
		MultiIntro ->
		m = modifiers_opt
		t = type
		id1 = IDENTIFIER
		"+." id2 = IDENTIFIER
		"(" p = formal_parameter_list_opt ")"
		b = BlockEval
		|
		m = modifiers_opt
		"void"
		id1 = IDENTIFIER
		"+." id2 = IDENTIFIER
		"(" p = formal_parameter_list_opt ")"
		b = BlockEval;		
	}*/
	
	private Map map;
	
	public MultiIntro(Position pos) {
		super(pos);
		this.map = new HashMap();
		System.out.println("Criou um objeto MultiIntro");
	}

	private static String className;

	// retorna nome da classe corrente
	public static String getClassName() {
		return className;
	}

	public List acceptCFG(CFGBuilder arg0, List arg1) {
		return arg1;
	}

	public Term entry() {
		return this;
	}

	public boolean add(String name, Object value) {
		if(!map.containsKey(name)) {
			map.put(name, value);
			return true;
		} else
			return false;
	}

	public Iterator iterator() {
		return map.keySet().iterator();
	}

	public Object get(String name) {
		return map.get(name);
	}
	
	public void set(String name, Object value) {
		if(map.containsKey(name)) {
			map.keySet();
			map.put(name, value);
		}
	}

	public TypeNode host() {
		return null;
	}

	public Expr thisReference(AJNodeFactory arg0, AJTypeSystem arg1) {
		// TODO Auto-generated method stub
		return null;
	}

	public void aspectMethodsEnter(AspectMethods arg0) {
		// TODO Auto-generated method stub		
	}

	public Node aspectMethodsLeave(AspectMethods arg0, AJNodeFactory arg1,
			AJTypeSystem arg2) {
		return this;
	}
	/*
	public Node visitChildren(NodeVisitor v) {
		//Acho que esse código pode ser gerado automaticamente e
		//suponho que seja genérico e funciona para todos os casos
		Iterator it = iterator();
		while(it.hasNext()) {
			String name = (String) it.next();
			Object ob = get(name);
			if(ob instanceof Node) {
				set(name, ((Node) ob).visit(v));
			}				
		}
		return this;
	}*/
	
	public Node desugar(NodeFactory ft, TypeSystem ts) {
		XAJNodeFactory nf = (XAJNodeFactory) ft;
		
		//Procura todas as classe que são subtipos de id1 (Expr no exemplo visitorxaj)
		//Para este exemplo, eu estou fazendo manualmente, porém
		//é necessário criar uma biblioteca para realizar essa operação
		// na AST.
		String names[] = new String[3];
		names[0] = "Sum";
		names[1] = "Mult";
		names[2] = "Number";
		
		Flags modifiers = (Flags) get("m");
		TypeNode returnType;
		if(get("t") != null)
			returnType = (TypeNode) get("t");
		else
			returnType = nf.CanonicalTypeNode(position(), ts.Void());
		String methodName = ((Identifier) get("id2")).getIdentifier();
		List/*<Formal>*/ param = (List/*<Formal>*/) get("p");
		BlockEval blockeval = (BlockEval) get("b");

		List list = new TypedList(new LinkedList(), ClassMember.class, false);
		
		/* Cria um intertype declaration para cada subclasse*/
		TypeNode classType;
		IntertypeMethodDecl intertype;
		List throwlist;
		BlockEval block;
		for(String className : names) {
			Eval.put("className", className);
			classType = nf.AmbTypeNode(position(), className);
			throwlist = new TypedList(new LinkedList(), TypeNode.class, false);
			block = (BlockEval) blockeval.copy();
			intertype = nf.IntertypeMethodDecl(position(), modifiers,
					(TypeNode) returnType.copy(), classType, methodName,
					param, throwlist, ((Block) block.desugar(ft, ts)));
			list.add(intertype);
		}
		
		//Criar um novo Nó do tipo Lista
		ListNode listnode = nf.ListNode(position(), list);
		
		return listnode;
	}
}
